using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Linq;


namespace LightCAD.Three
{
    public class SkeletonHelper : LineSegments
    {
        #region scope properties or methods
        //private static Vector3 _vector = new Vector3();
        //private static Matrix4 _boneMatrix = new Matrix4();
        //private static Matrix4 _matrixWorldInv = new Matrix4();
        public SkeletonHelperContext getContext()
        {
            return ThreadContext.getCurrThreadContext().SkeletonHelperCtx;
        }
        private static JsArr<Bone> getBoneList(Object3D _object, JsArr<Bone> boneList = null)
        {
            if (boneList == null)
            {
                boneList = new JsArr<Bone>();
            }

            if (_object is Bone)
            {
                boneList.push(_object as Bone);
            }

            for (int i = 0; i < _object.children.length; i++)
            {
                getBoneList(_object.children[i], boneList);
            }
            return boneList;
        }
        #endregion

        #region Properties

        public Object3D root;
        public JsArr<Bone> bones;

        #endregion

        #region constructor
        public SkeletonHelper(Object3D _object)
            : this(makeGeometry(_object))
        {
            this.type = "SkeletonHelper";
            this.root = _object;
            this.matrix = _object.matrixWorld;
            this.matrixAutoUpdate = false;
        }

        public SkeletonHelper(Tuple<BufferGeometry, JsArr<Bone>> result)
            : base(result.Item1, new LineBasicMaterial(new MaterialValues() { vertexColors = true, depthTest = false, depthWrite = false, toneMapped = false, transparent = true }))
        {
            this.bones = result.Item2;
        }

        private static Tuple<BufferGeometry, JsArr<Bone>> makeGeometry(Object3D _object)
        {
            var bones = getBoneList(_object);
            var geometry = new BufferGeometry();
            var vertices = new JsArr<double>();
            var colors = new JsArr<double>();
            var color1 = new Color(0, 0, 1);
            var color2 = new Color(0, 1, 0);
            for (int i = 0; i < bones.length; i++)
            {
                var bone = bones[i];
                if (bone.parent != null && bone.parent is Bone)
                {
                    vertices.push(0, 0, 0);
                    vertices.push(0, 0, 0);
                    colors.push(color1.r, color1.g, color1.b);
                    colors.push(color2.r, color2.g, color2.b);
                }
            }
            geometry.setAttribute("position", new Float32BufferAttribute(vertices.ToArray(), 3));
            geometry.setAttribute("color", new Float32BufferAttribute(colors.ToArray(), 3));
            return Tuple.Create(geometry, bones);
        }
        #endregion

        #region methods
        public override void updateMatrixWorld(bool force)
        {
            var ctx = getContext();
            var _matrixWorldInv = ctx._matrixWorldInv;
            var _boneMatrix = ctx._boneMatrix;
            var _vector = ctx._vector;
            var bones = this.bones;
            var geometry = this.geometry;
            var position = geometry.getAttribute("position");
            _matrixWorldInv.copy(this.root.matrixWorld).invert();
            for (int i = 0, j = 0; i < bones.length; i++)
            {
                var bone = bones[i];
                if (bone.parent != null && bone.parent is Bone)
                {
                    _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.matrixWorld);
                    _vector.setFromMatrixPosition(_boneMatrix);
                    position.setXYZ(j, _vector.x, _vector.y, _vector.z);
                    _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.parent.matrixWorld);
                    _vector.setFromMatrixPosition(_boneMatrix);
                    position.setXYZ(j + 1, _vector.x, _vector.y, _vector.z);
                    j += 2;
                }
            }
            geometry.getAttribute("position").needsUpdate = true;
            base.updateMatrixWorld(force);
        }
        public void dispose()
        {
            this.geometry.dispose();
            this.material.dispose();
        }
        #endregion

    }
}
